Skip to content

Stop reporting expected Admin API failures from store execute as CLI bugs#7406

Open
dmerand wants to merge 1 commit intomainfrom
dlm-store-execute-error-classify
Open

Stop reporting expected Admin API failures from store execute as CLI bugs#7406
dmerand wants to merge 1 commit intomainfrom
dlm-store-execute-error-classify

Conversation

@dmerand
Copy link
Copy Markdown
Contributor

@dmerand dmerand commented Apr 27, 2026

WHY are these changes introduced?

Fixes shop/issues#32996.

shopify store execute is creating Observe error-analytics issues (403 events / 113 affected users in the last 7 days) for two error shapes that are not CLI bugs:

  1. GraphQL Error (Code: 402): "Unavailable Shop" — the shop is frozen / on hold; this is a store-state issue.
  2. The user aborted a request. — surfaces when an AbortController signal fires (CLI-side fetch timeout or user cancel).

Both were ending up wrapped by fetchApiVersions's "unknown error" branch in cli-kit, which throws BugError("Unknown error connecting…") and destroys the original error's type. The version lookup runs on every store execute startup, so both shapes show up reliably as bugs in error analytics.

WHAT is this pull request doing?

The fix is local to store execute — that's where the Observe noise was reported, and the recovery copy is store-flavored. Other Admin API callers (app, theme, hydrogen) keep their existing behavior. cli-kit is untouched.

store execute now drives its API-version lookup directly via graphqlRequest against an inline publicApiVersions query — the same primitive the execute-phase request already uses. With both phases on the same primitive, they see the same raw ClientError / Error shapes, so a single shared classifier (file-private to admin-transport.ts) covers both.

File Change
store/.../execute/admin-context.ts Pure orchestration: load session → ask transport for versions → validate → build context. No GraphQL or classification details.
store/.../execute/admin-transport.ts Owns both Admin GraphQL fetches (fetchPublicApiVersions + runAdminStoreGraphQLOperation). 402 → AbortError("The store … is currently unavailable."); user-aborted fetches → AbortError("Request to … was aborted before it completed."); 401/404 still trigger the existing stored-auth re-auth path via structural status check.
store/.../execute/admin-context.test.ts Mocks fetchPublicApiVersions; covers orchestration only.
store/.../execute/admin-transport.test.ts Covers both transport functions and every classification branch (parameterized over ABORTED_FETCH_MESSAGE_FRAGMENTS so adding a new abort shape auto-extends coverage). Includes a 402-with-errors case that locks down the branch ordering.

Behavior preserved from before this PR: when the Admin API returns a GraphQL errors payload, runAdminStoreGraphQLOperation throws AbortError("GraphQL operation failed.") — process exits non-zero. This is unchanged. The closest sibling, packages/app/.../execute-operation.ts, instead renders GraphQL errors via renderError and returns void (zero exit). Aligning the two is out of scope for this PR; we keep store execute’s existing exit-code semantics.

How to test your changes?

Unit tests:

cd packages/store && ./node_modules/.bin/vitest run src/cli/services/store/execute

The admin-transport.test.ts suite covers every error shape from the bug report: 402 in the versions phase, 402 with errors: [...] in the execute phase, each abort-message fragment, abort-by-name, and 401 in both phases.

Manual smoke (against any working store, to confirm no regression):

pnpm run shopify store execute --store <shop>.myshopify.com --query 'query { shop { name id } }'

To exercise the new 402 branch you need a frozen / unavailable store; if you have one, the CLI should surface a clean AbortError("The store … is currently unavailable.") with recovery guidance instead of the previous BugError("Unknown error connecting…").

Post-release steps

None.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact (this change reduces Observe noise in the cli slice for store execute; visible within a few days post-release)
  • No changeset added: this fix only changes how already-thrown errors are classified (renames a BugError to an AbortError with cleaner copy). Existing @shopify/store consumers see the same exit code on the same input; they were already trapping or reporting these errors. The user-facing improvement is the reduced Observe noise / cleaner CLI message, not a published API change.

Copy link
Copy Markdown
Contributor Author

dmerand commented Apr 27, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@dmerand dmerand force-pushed the dlm-store-execute-error-classify branch 10 times, most recently from ddfc7b3 to 02e2b80 Compare April 27, 2026 18:52
@dmerand dmerand marked this pull request as ready for review April 27, 2026 19:04
@dmerand dmerand requested a review from a team as a code owner April 27, 2026 19:04
@dmerand dmerand requested review from Copilot and isaacroldan April 27, 2026 19:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces error-analytics noise for shopify store execute by avoiding cli-kit's fetchApiVersions “unknown error” wrapping and instead performing Admin API version discovery via the same graphqlRequest primitive used for the execute-phase request, enabling consistent error-shape classification.

Changes:

  • Added a store-execute–local fetchPublicApiVersions (Admin GraphQL) and centralized error classification for 402 (store unavailable) and aborted fetches.
  • Updated context creation to use the new transport function for version resolution (keeping orchestration in admin-context.ts).
  • Expanded/added unit tests to cover both transport functions and the relevant classification branches.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/store/src/cli/services/store/execute/admin-transport.ts Adds version-discovery GraphQL request + shared error classifier to map 402/abort shapes to user-facing AbortErrors and preserve stored-auth reauth behavior.
packages/store/src/cli/services/store/execute/admin-transport.test.ts Adds comprehensive transport-level test coverage for success paths, auth invalidation, 402 ordering, and aborted-fetch classification.
packages/store/src/cli/services/store/execute/admin-context.ts Switches version resolution from cli-kit fetchApiVersions to the new transport-owned fetchPublicApiVersions.
packages/store/src/cli/services/store/execute/admin-context.test.ts Updates tests to mock fetchPublicApiVersions and focus coverage on orchestration behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/store/src/cli/services/store/execute/admin-transport.ts Outdated
Comment thread packages/store/src/cli/services/store/execute/admin-transport.test.ts Outdated
…bugs

Two error shapes from the Admin API were being misclassified as
`BugError` and reported to Observe (issue shop/issues#32996):

- HTTP 402 "Unavailable Shop" \u2014 the shop is frozen / on hold; this is a
  store-state issue, not a CLI bug.
- "The user aborted a request." \u2014 surfaces from node-fetch when an
  AbortController signal fires (CLI-side fetch timeout or user cancel).

Both were getting wrapped by `fetchApiVersions`'s "unknown error" catch
in cli-kit, which destroys the original error type. To classify them
locally without reaching past the public API, `store execute` now drives
its API-version lookup directly via `graphqlRequest` against an inline
`publicApiVersions` query \u2014 the same primitive `admin-transport.ts` uses
for the user's query. Both phases now see raw `ClientError` /
`Error` shapes and share a single classifier in `admin-errors.ts`.

Other commands that hit the Admin API on startup are unaffected; cli-kit
is untouched.

- `packages/store/.../execute/admin-errors.ts` (new): structural
  `ClientError` shape check, abort detection, and `classifyAdminApiError`
  that maps known non-bug shapes to user-facing `AbortError`s.
- `packages/store/.../execute/admin-context.ts`: drives the version
  lookup via `graphqlRequest` directly. 401/404 still trigger the
  re-auth path (now via structural status check rather than parsing
  message strings); 402 and aborts go through the shared classifier.
- `packages/store/.../execute/admin-transport.ts`: same shared
  classifier covers aborts on the execute-phase request.

New `admin-errors.test.ts` covers the classifier; existing context and
transport tests updated to mock `graphqlRequest` directly.
@dmerand dmerand force-pushed the dlm-store-execute-error-classify branch from 02e2b80 to 4b99cef Compare April 27, 2026 19:12
// node-fetch surfaces) so a fetch that bubbles past cli-kit's retry layer is still
// classified correctly. Exported so tests reference the same source of truth as
// production.
export const ABORTED_FETCH_MESSAGE_FRAGMENTS = ['the user aborted a request', 'the operation was aborted'] as const
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have errorMessageImpliesEnvironmentIssue in error.ts in cli-kit, maybe you want to reuse that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants